import axios from 'axios'
import type {
  AxiosInstance,
  AxiosError,
  AxiosRequestConfig,
  AxiosResponse,
  InternalAxiosRequestConfig
} from 'axios'
import { AxiosCanceler } from './axiosCancel'
import type { ResultData } from '@/api/interface'
import { useGlobalStore } from '@/stores/modules/global'
import router from '@/router'
import { isType } from '@/utils'

const axiosCanceler = new AxiosCanceler()

const toLogin = () => {
  router.push('/login')
}

class RequestHttp {
  service: AxiosInstance
  public constructor(config: AxiosRequestConfig) {
    this.service = axios.create(config)
    // 请求拦截器
    this.service.interceptors.request.use(
      (config: InternalAxiosRequestConfig) => {
        axiosCanceler.addPending(config)
        const globalStore = useGlobalStore()
        config.timeout = 10000 //10秒超时
        if (globalStore.token) config.headers.Authorization = globalStore.token

        return config
      },
      (error: AxiosError) => {
        return Promise.reject(error)
      }
    )

    // 响应拦截器
    this.service.interceptors.response.use(
      (response: AxiosResponse) => {
        const { data, config, status } = response
        if (status == 200 && isType(data) == 'blob') {
          return data
        }
        const globalStore = useGlobalStore()
        axiosCanceler.removePending(config)
        if (data.code == 401 || data.code == 402) {
          // token无效 || token为空
          ElMessage.error(data.msg)
          globalStore.setToken('')
          toLogin()
          return Promise.reject(data)
        }
        if (data.code !== 200) {
          ElMessage.error(data.msg || `未知错误（code:${data.code}）`)
          return Promise.reject(data)
        }
        return data
      },
      (error: AxiosError) => {
        const { response } = error
        if (error.message.indexOf('timeout') !== -1) ElMessage.error('请求超时')
        if (response) ElMessage.error('请求失败')
        // 服务器结果都没有返回(可能服务器错误可能客户端断网)，断网处理:可以跳转到断网页面
        if (!window.navigator.onLine) router.replace({ path: '/500' })
        return Promise.reject(error)
      }
    )
  }

  /**
   * @description 常用请求方法封装
   */
  /** get请求 */
  get<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
    return this.service.get(url, { params, ..._object })
  }
  /** get请求带路径参数，如请求地址/api/{id}，参数{id:123} */
  getCarryPathVariable<T>(
    url: string,
    params?: any,
    pathVariables?: any,
    _object = {}
  ): Promise<ResultData<T>> {
    let newRequestPath = url
    Object.keys(pathVariables).forEach(function (key) {
      newRequestPath = newRequestPath.replace('{' + key + '}', pathVariables[key])
    })
    return this.service.get(newRequestPath, { params, ..._object })
  }
  /** postJson */
  post<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
    return this.service.post(url, params, _object)
  }
  postForm<T>(url: string, params?: any, _object = {}): Promise<ResultData<T>> {
    const urlSearchParams = new URLSearchParams()
    for (const i in params) {
      urlSearchParams.append(i, params[i])
    }
    _object = {
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
      }
    }
    return this.service.post(url, urlSearchParams, _object)
  }
  postFormCarryPathVariable<T>(
    url: string,
    params?: any,
    pathVariables?: any,
    _object = {}
  ): Promise<ResultData<T>> {
    let newRequestPath = url
    Object.keys(pathVariables).forEach(function (key) {
      newRequestPath = newRequestPath.replace('{' + key + '}', pathVariables[key])
    })
    const urlSearchParams = new URLSearchParams()
    for (const i in params) {
      urlSearchParams.append(i, params[i])
    }
    _object = {
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
      }
    }
    return this.service.post(newRequestPath, urlSearchParams, _object)
  }
  postFormData<T>(url: string, params?: any, _object = {}): Promise<ResultData<T>> {
    const formData = new FormData()
    for (const i in params) {
      formData.append(i, params[i])
    }
    _object = {
      headers: {
        'Content-Type': 'multipart/form-data'
      }
    }
    return this.service.post(url, formData, _object)
  }
}

export default RequestHttp
